<?php
class Chat_lib {
	
	protected $ci;
	public $connection_timeout = 30;
	public $namecookie = "365bustour";
	public $usercookie = "365bustour_chatuserid";
	public $state_queue = 0;
	public $state_waiting = 1;
	public $state_chatting = 2;
	public $state_closed = 3;
	public $state_loading = 4;
	public $state_left = 5;
	
	public $kind_user = 1;
	public $kind_agent = 2;
	public $kind_for_agent = 3;
	public $kind_info = 4;
	public $kind_conn = 5;
	public $kind_events = 6;
	public $kind_avatar = 7;
	
	public function __construct()
	{
		$this->ci =& get_instance();
	}
	public function kind_to_string()
	{
		return array($this->kind_user => "user", $this->kind_agent => "agent", $this->kind_for_agent => "hidden",
								$this->kind_info => "inf", $this->kind_conn => "conn", $this->kind_events => "event", $this->kind_avatar => "avatar");
	}
	function ban_for_addr_($addr)
	{
		$this->ci->load->model('chat/ban_model', 'ban_m');
		return $this->ci->ban_m->ban_for_addr_($addr);
	}

	function get_user_id()
	{
		return (time() + microtime()) . rand(0, 99999999);
	}
	
	function next_token()
	{
		return rand(99999, 99999999);
	}
	
	function next_revision()
	{
		$this->ci->load->model('chat/revision_model', 'revision_m');
		return $this->ci->revision_m->next_revision();
	}
	
	
	function commit_thread($threadid, $params)
	{
// 		$data = array('lrevision' =>  $this->next_revision(), 'dtmmodified' => 'CURRENT_TIMESTAMP');
		$data = array('lrevision' =>  $this->next_revision(), 'dtmmodified' => date('Y-m-d H:i:s'));
		$this->ci->load->model('chat/thread_model', 'thread_m');
		foreach ($params as $k => $v) {
			$data[$k] = $v;
		}
		$this->ci->thread_m->commit_thread($threadid, $data);
	}
	
	function get_remote_level($useragent)
	{
		$knownAgents = array("opera", "msie", "chrome", "safari", "firefox", "netscape", "mozilla");
		$useragent = strtolower($useragent);
		foreach ($knownAgents as $agent) {
			if (strstr($useragent, $agent)) {
				if (preg_match("/" . $agent . "[\\s\/]?(\\d+(\\.\\d+)?)/", $useragent, $matches)) {
					$ver = $matches[1];
	
					if ($this->is_ajax_browser($agent, $ver, $useragent))
						return "ajaxed";
					else if ($this->is_old_browser($agent, $ver))
						return "old";
	
					return "simple";
				}
			}
		}
		return "simple";
	}
	
	function is_agent_opera95()
	{
		$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
		if (strstr($useragent, "opera")) {
			if (preg_match("/opera[\\s\/]?(\\d+(\\.\\d+)?)/", $useragent, $matches)) {
				$ver = $matches[1];
	
				if ($ver >= "9.5")
					return true;
			}
		}
		return false;
	}
	
	function is_mac_opera()
	{
		$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
		return strstr($useragent, "opera") && strstr($useragent, "mac");
	}
	
	function needsFramesrc()
	{
		$useragent = strtolower($_SERVER['HTTP_USER_AGENT']);
		return strstr($useragent, "safari/");
	}
	
	function is_secure_request()
	{
		return
		isset($_SERVER['SERVER_PORT']) && $_SERVER['SERVER_PORT'] == '443'
		|| isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == "on"
		|| isset($_SERVER["HTTP_HTTPS"]) && $_SERVER["HTTP_HTTPS"] == "on";
	}
	
	function is_ajax_browser($browserid, $ver, $useragent)
	{
		if ($browserid == "opera")
			return $ver >= 8.02;
		if ($browserid == "safari")
			return $ver >= 125;
		if ($browserid == "msie")
			return $ver >= 5.5 && !strstr($useragent, "powerpc");
		if ($browserid == "netscape")
			return $ver >= 7.1;
		if ($browserid == "mozilla")
			return $ver >= 1.4;
		if ($browserid == "firefox")
			return $ver >= 1.0;
		if ($browserid == "chrome")
			return true;
	
		return false;
	}
	
	function thread_by_id_($id)
	{
		$this->ci->load->model('chat/thread_model', 'thread_m');
		return $this->ci->thread_m->thread_by_id_($id);
	}
	
	function reopen_thread($threadid)
	{
		$thread = $this->thread_by_id_($threadid);
		if (!$thread)
			return FALSE;
	
		if ($thread['istate'] == $this->state_closed || $thread['istate'] == $this->state_left)
			return FALSE;
	
		if ($thread['istate'] != $this->state_chatting && $thread['istate'] != $this->state_queue && $thread['istate'] != $this->state_loading) {
			$this->commit_thread($threadid,
						  array("istate" => $this->state_waiting, "nextagent" => 0));
		}
	
		$this->post_message_($thread['threadid'], $this->kind_events, lang('chat.status.user.reopenedthread'));
		return $thread;
	}
	function post_message_($threadid, $kind, $message,$from = null, $utime = null, $opid = null)
	{
		$data = array(
			'threadid' => $threadid,
			'ikind' => $kind,
			'tmessage' => $message,
			'tname' => $from,
			'agentId' => $opid ? $opid : "0",
// 			'dtmcreated' => $utime ? "FROM_UNIXTIME($utime)" : "CURRENT_TIMESTAMP"
			'dtmcreated' => $utime ? date('Y-m-d H:i:s', $utime) : date('Y-m-d H:i:s')
		);
		$this->ci->load->model('chat/message_model', 'message_m');
		return $this->ci->message_m->post_message_($data);
	}
	function notify_operator_alive($operatorid, $istatus)
	{
		$this->ci->load->model('chat/operator_model', 'operator_m');
		$this->ci->operator_m->update($operatorid, array(
				'istatus' => $istatus,
				'dtmlastvisited' => date('Y-m-d H:i:s')
		), FALSE);
	}
	function visitor_from_request()
	{
		$defaultName = lang("chat.default.username");
		$userName = $defaultName;
		if (isset($_COOKIE[$this->namecookie])) {
			$data = base64_decode(strtr($_COOKIE[$this->namecookie], '-_,', '+/='));
			if (strlen($data) > 0) {
				$userName = strip_tags($data);
			}
		}
		//fixed future
// 		if ($userName == $defaultName) {
// 			$userName = getgetparam('name', $userName);
// 		}
	
		if (isset($_COOKIE[$this->usercookie])) {
			$userId = $_COOKIE[$this->usercookie];
		} else {
			$userId = $this->get_user_id();
			setcookie($this->usercookie, $userId, time() + 60 * 60 * 24 * 365);
		}
		return array('id' => $userId, 'name' => $userName);
	}
	function get_remote_host()
	{
		$extAddr = $_SERVER['REMOTE_ADDR'];
		if (isset($_SERVER['HTTP_X_FORWARDED_FOR']) &&
				$_SERVER['HTTP_X_FORWARDED_FOR'] != $_SERVER['REMOTE_ADDR']) {
			$extAddr = $_SERVER['REMOTE_ADDR'] . ' (' . $_SERVER['HTTP_X_FORWARDED_FOR'] . ')';
		}
		return isset($_SERVER['REMOTE_HOST']) ? $_SERVER['REMOTE_HOST'] : $extAddr;
	}
	
	function check_connections_from_remote($remote)
	{
		if ($this->ci->config->item('max_connections_from_one_host', 'chat') == 0) {
			return true;
		}
		$this->ci->load->model('chat/thread_model', 'thread_m');
		$result = $this->ci->thread_m->check_connections_from_remote($remote, $this->state_closed, $this->state_left);
		if ($result) {
			return $result < $this->ci->config->item('max_connections_from_one_host', 'chat');
		}
		return true;
	}
	function create_thread($groupid, $username, $remoteHost, $referer, $lang, $userid, $userbrowser, $initialState)
	{
		$data = array(
				'userName' => $username,
				'userid' => $userid,
				'ltoken' => $this->next_token(),
				'remote' => $remoteHost,
				'referer' => $referer,
				'lrevision' => $this->next_revision(),
				'locale' => $lang,
				'userAgent' => $userbrowser,
// 				'dtmcreated' => 'CURRENT_TIMESTAMP',
// 				'dtmmodified' => 'CURRENT_TIMESTAMP',
				'dtmcreated' => date('Y-m-d H:i:s'),
				'dtmmodified' => date('Y-m-d H:i:s'),
				'istate' => $initialState,
		);
		if($groupid){
			$data['groupid'] = $groupid;
		}
		$this->ci->db->set($data, FALSE);
		$this->ci->db->insert('chatthread');
		$id = $this->ci->db->insert_id();
	
		$newthread = $this->thread_by_id_($id);
		return $newthread;
	}
	
	function ping_thread($thread, $isuser, $istyping)
	{
// 		$params = array(($isuser ? "lastpinguser" : "lastpingagent") => "CURRENT_TIMESTAMP",
		$params = array(($isuser ? "lastpinguser" : "lastpingagent") => date('Y-m-d H:i:s'),
				($isuser ? "userTyping" : "agentTyping") => ($istyping ? "1" : "0"));
		$lastping = $thread[$isuser ? "lpagent" : "lpuser"];
		$current = $thread['current'];
		
		
		if ($thread['istate'] == $this->state_loading && $isuser) {
			$params['istate'] = $this->state_queue;
			$this->commit_thread($thread['threadid'], $params);
			return;
		}
		if ($lastping > 0 && abs($current - $lastping) > $this->connection_timeout) {
			$params[$isuser ? "lastpingagent" : "lastpinguser"] = "0";
			if (!$isuser) {
				$message_to_post = lang("chat.status.user.dead");
				$this->post_message_($thread['threadid'], $this->kind_for_agent, $message_to_post, null, $lastping + $this->connection_timeout);
			} else if ($thread['istate'] == $this->state_chatting) {
	
				$message_to_post = lang("chat.status.operator.dead");
				$this->post_message_($thread['threadid'], $this->kind_conn, $message_to_post, null, $lastping + $this->connection_timeout);
				$params['istate'] = $this->state_waiting;
				$params['nextagent'] = 0;
				$this->commit_thread($thread['threadid'], $params);
				return;
			}
		}
	
		$this->update_thread_access($thread['threadid'], $params);
	}
	
	function update_thread_access($threadid, $params)
	{
		$this->ci->load->model('chat/thread_model', 'thread_m');
		$data = array();
		foreach ($params as $k => $v) {
			$data[$k] = $v;
		}
		
		$this->ci->thread_m->update($threadid, $data, FALSE);
	}
	
	function close_thread($thread, $isuser)
	{
		if ($thread['istate'] != $this->state_closed) {
			$this->ci->load->model('chat/message_model', 'message_m');
			$this->commit_thread($thread['threadid'], array('istate' => $this->state_closed,
					'messageCount' => $this->ci->message_m->get_message_count_onclose($thread['threadid'])));
		}
		
		$message = $isuser ? lang("chat.status.user.left", $thread['userName'])
		: lang("chat.status.operator.left", $thread['agentName']);
		$this->post_message_($thread['threadid'], $this->kind_events, $message);
	}
	
	function check_for_reassign($thread, $operator)
	{
		$operatorName = $operator['vclocalename'] ;
		if ($thread['istate'] == $this->state_waiting &&
				($thread['nextagent'] == $operator['operatorid']
						|| $thread['agentId'] == $operator['operatorid'])) {
			$this->do_take_thread($thread['threadid'], $operator['operatorid'], $operatorName);
			if ($operatorName != $thread['agentName']) {
				$message_to_post = sprintf(lang("chat.status.operator.changed"), $operatorName, $thread['agentName']);
			} else {
				$message_to_post = sprintf(lang("chat.status.operator.returned", $operatorName));
			}
	
			$this->post_message_($thread['threadid'], $this->kind_events, $message_to_post);
// 			$this->post_message_($thread['threadid'], $this->kind_avatar, $operator['vcavatar'] ? $operator['vcavatar'] : "");
		}
	}
	function do_take_thread($threadid, $operatorId, $operatorName)
	{
		$this->commit_thread($threadid,
				array("istate" => $this->state_chatting,
						"nextagent" => 0,
						"agentId" => $operatorId,
						"agentName" => "'" . $operatorName . "'"));
	}
	
	function get_messages($threadid, $meth, $isuser, $lastid)
	{
		$this->ci->load->model('chat/message_model', 'message_m');
		$this->ci->load->library('chat/common_lib');
		$msgs = $this->ci->message_m->get_messages($threadid, $meth, $isuser, $lastid, $this->kind_for_agent);
		$messages = array();
		foreach ($msgs as $msg) {
			$message = "";
			if ($meth == 'xml') {
				switch ($msg['ikind']) {
					case $this->kind_avatar:
						$message = "<avatar>" . $this->ci->common_lib->escape_with_cdata($msg['tmessage']) . "</avatar>";
						break;
					default:
						$message = "<message>" . $this->ci->common_lib->escape_with_cdata($this->message_to_html($msg)) . "</message>\n";
				}
			} else {
				if ($msg['ikind'] != $this->kind_avatar) {
					$message = (($meth == 'text') ? $this->message_to_text($msg) : $this->message_to_html($msg));
				}
			}
	
			$messages[] = $message;
			if ($msg['messageid'] > $lastid) {
				$lastid = $msg['messageid'];
			}
		}
	
		return array($messages, $lastid);
	}
	
	function print_thread_messages($thread, $token, $lastid, $isuser, $format, $agentid = null)
	{
		$threadid = $thread['threadid'];
		$istyping = abs($thread['current'] - $thread[$isuser ? "lpagent" : "lpuser"]) < $this->connection_timeout
		&& $thread[$isuser ? "agentTyping" : "userTyping"] == "1" ? "1" : "0";
		$this->ci->load->library('chat/common_lib');
		
		if ($format == "xml") {
			list($output, $return_lastid) = $this->get_messages($threadid, "xml", $isuser, $lastid);
	
			$lastid = $return_lastid;
			$this->ci->common_lib->start_xml_output();
			print("<thread lastid=\"$lastid\" typing=\"" . $istyping . "\" canpost=\"" . (($isuser || $agentid != null && $agentid == $thread['agentId']) ? 1 : 0) . "\">");
			foreach ($output as $msg) {
				print $msg;
			}
			print("</thread>");
		} else if ($format == "html") {
			list($output, $return_lastid) = $this->get_messages($threadid, "html", $isuser, $lastid);
			$lastid = $return_lastid;
			$this->ci->common_lib->start_html_output();
// 			$url = "$webimroot/thread.php?act=refresh&amp;thread=$threadid&amp;token=$token&amp;html=on&amp;user=" . ($isuser ? "true" : "false");
	
			print(
					"<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">" .
					"<html>\n<head>\n" .
					"<link href=\"". css_url('home/chat/chat.css'). "\" rel=\"stylesheet\" type=\"text/css\">\n" .
					//"<meta http-equiv=\"Refresh\" content=\"" . $settings['updatefrequency_oldchat'] . "; URL=$url&amp;sn=11\">\n" .
					"<meta http-equiv=\"Pragma\" content=\"no-cache\">\n" .
					"<title>chat</title>\n" .
					"</head>\n" .
					"<body bgcolor='#FFFFFF' text='#000000' link='#C28400' vlink='#C28400' alink='#C28400' onload=\"if( location.hash != '#aend' ){location.hash='#aend';}\">" .
					"<table width='100%' cellspacing='0' cellpadding='0' border='0'><tr><td valign='top' class='message'>");
	
			foreach ($output as $msg) {
				print $msg;
			}
	
			print(
					"</td></tr></table><a name='aend'></a>" .
					"</body></html>");
		}
	}
	function message_to_html($msg)
	{
		if ($msg['ikind'] == $this->kind_avatar) return "";
		$message = "<span>" . date("H:i:s", $msg['created']) . "</span> ";
		$kind = $this->kind_to_string();

		if ($msg['tname'])
			$message .= "<span class='n".$kind[$msg['ikind']]."'>" . htmlspecialchars($msg['tname']) . "</span>: ";
		$message .= "<span class='m$kind'>" . $this->prepare_html_message($msg['tmessage']) . "</span><br/>";
		return $message;
	}
	
	function message_to_text($msg)
	{
		if ($msg['ikind'] == $this->kind_avatar) return "";
		$message_time = date("H:i:s ", $msg['created']);
		if ($msg['ikind'] == $this->kind_user || $msg['ikind'] == $this->kind_agent) {
			if ($msg['tname'])
				return $message_time . $msg['tname'] . ": " . $msg['tmessage'] . "\n";
			else
				return $message_time . $msg['tmessage'] . "\n";
		} else if ($msg['ikind'] == $this->kind_info) {
			return $message_time . $msg['tmessage'] . "\n";
		} else {
			return $message_time . "[" . $msg['tmessage'] . "]\n";
		}
	}
	function prepare_html_message($text)
	{
		$escaped_text = htmlspecialchars($text);
		$text_w_links = preg_replace('/(http|ftp):\/\/\S*/', '<a href="$0" target="_blank">$0</a>', $escaped_text);
		$multiline = str_replace("\n", "<br/>", $text_w_links);
		return $multiline;
	}
	
	function take_thread($thread, $operator)
	{
		$state = $thread['istate'];
		$threadid = $thread['threadid'];
		$message_to_post = "";
	
		$operatorName = $operator['vclocalename'];
	
		if ($state == $this->state_queue || $state == $this->state_waiting || $state == $this->state_loading) {
			$this->do_take_thread($threadid, $operator['operatorid'], $operatorName);
	
			if ($state == $this->state_waiting) {
				if ($operatorName != $thread['agentName']) {
					$message_to_post = lang("chat.status.operator.changed", $operatorName, $thread['agentName']);
				} else {
					$message_to_post = lang("chat.status.operator.returned", $operatorName);
				}
			} else {
				$message_to_post = lang("chat.status.operator.joined", $operatorName);
			}
		} else if ($state == $this->state_chatting) {
			if ($operator['operatorid'] != $thread['agentId']) {
				$this->do_take_thread($threadid, $operator['operatorid'], $operatorName);
				$message_to_post = lang("chat.status.operator.changed", $operatorName, $thread['agentName']);
			}
		} else {
			die("cannot take thread");
		}
	
		if ($message_to_post) {
			$this->post_message_($threadid, $this->kind_events, $message_to_post);
// 			$this->post_message_($threadid, $this->kind_avatar, $operator['vcavatar'] ? $operator['vcavatar'] : "");
		}
	}
}
?>